<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>CANVAS-阿凡达-树精灵</title>
	<style>
		* {
			margin: 0;
			padding: 0;
		}

		html,
		body {
			height: 100%;
		}

		body {
			overflow: hidden;
			transform-style: preserve-3d;
		}

		.box {
			position: absolute;
			width: 100%;
			height: 100%;
			transform-style: preserve-3d;
			display: flex;
			justify-content: center;
			align-items: center;
		}

		.elf {
			position: absolute;
			transform-origin: left center;
			width: 0;
			height: 0;
			transform-style: preserve-3d;
			animation: rotate 29s linear infinite;
		}

		@keyframes rotate {
			0% {
				transform: rotateY(-30deg) rotateX(30deg);
			}

			50% {
				transform: rotateY(30deg) rotateX(60deg);
			}

			100% {
				transform: rotateY(-30deg) rotateX(30deg);
			}
		}

		.inner {
			position: absolute;
			transform-style: preserve-3d;
			animation: lr 4s ease-in-out infinite;
		}

		@keyframes lr {
			0% {
				transform: translateX(-30px);
			}

			50% {
				transform: translateX(30px);
			}

			100% {
				transform: translateX(-30px);
			}
		}

		.outer {
			position: absolute;
			transform-style: preserve-3d;
			animation: ud 19s ease-in-out infinite;
		}

		@keyframes ud {
			0% {
				transform: translateY(-120px);
			}

			50% {
				transform: translateY(120px);
			}

			100% {
				transform: translateY(-120px);
			}
		}
	</style>
</head>

<body bgcolor="#000000">
	<div class="box">
		<div class="outer">
			<div class="inner">
				<div class="elf"> </div>
			</div>
		</div>
	</div>
</body>
<script>
	class Elf {
		constructor(el, deg) {
			this.el = el
			this.w = 150 // canvas width
			this.h = 150
			this.lineWidth = 2
			this.len = 90 // 波长
			this.height = 20 // 幅度
			this.start = 0 // 起始角度
			this.speed = 0.06 // 速度
			this.count = 12

			for (let i = 0; i < this.count; i++) {
				this.tentacle(~~(360 / this.count * i) + 9)
			}
			this.update()
		}
		createCanvas(deg = 0) {
			let canvas = document.createElement('canvas')
			canvas.width = this.w
			canvas.height = this.h
			canvas.style.position = 'absolute'
			canvas.style.left = '0'
			canvas.style.transformOrigin = 'left center'
			canvas.style.transform = 'rotateY(' + deg + 'deg)'
			let ctx = canvas.getContext('2d')

			this.el.appendChild(canvas)
			ctx.lineWidth = this.lineWidth
			ctx.strokeStyle = 'rgba(255,255,255,.8)'
			return ctx
		}

		update() {
			const step = () => {
				this.start += this.speed
				requestAnimationFrame(step)
			}
			requestAnimationFrame(step)
		}

		tentacle(deg) {
			let ctx = this.createCanvas(deg)
			let len = 110 + ~~(Math.random() * 10)

			const step = () => {
				this.draw(ctx, len)
				requestAnimationFrame(step)
			}
			requestAnimationFrame(step)
		}
		draw(ctx, len) {
			ctx.clearRect(0, 0, this.w, this.h)
			let deg = this.start
			let x = 0
			let y = Math.sin(deg - Math.PI / this.len * 0) * this.height + this.height * 2 - this.height * 0 / 100
			ctx.beginPath()
			ctx.lineWidth = 2
			ctx.moveTo(x, y)
			ctx.lineTo(x + 1, y + 40)
			ctx.stroke()
			let _deg = 0
			for (let i = 0; i < len; i++) {
				ctx.save()
				_deg = Math.min(_deg += 0.5, 180)
				ctx.rotate(_deg * Math.PI / 180)
				ctx.beginPath()
				ctx.lineWidth = 1 + 3 / len * (len - i)
				ctx.moveTo(x, y)
				y = Math.sin(deg - Math.PI / this.len * i) * this.height + this.height * 2 - this.height * i / 100
				x = i
				ctx.lineTo(x, y)
				if (i == len - 1) {
					ctx.arc(x, y, 1.2, 0, Math.PI * 2)
				}
				ctx.stroke()

				ctx.restore()

			}
		}
	}

	new Elf(document.querySelector('.elf'))

</script>

</html>